Hier mchte ich euch meinen INTERVAL Ersatzstoff vorstellen.

Wer gar nicht wei, worum es sich dreht, sollte dieses lesen:
[url=https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay]Blink Without Delay[/url]
[url=https://forum.arduino.cc/index.php?topic=423688.0]Der Wachmann[/url]

Hlt man sich an die dort vorgeschlagenen Wege, was auch gut und richtig ist, dann wird der Quellcode mit solchen Statements geflutet:
[code]
if(millis() - gemerkteZeit >= wunschLaufzeit)
{
  // tuwas
  gemerkteZeit = millis();
}
[/code]
Die Wurst ist total korrekt.
Aber dennoch unangenehm, da mit viel Schreibarbeit verbunden.
Es schleichen sich schnell Copy+Paste Fehler ein.
Der Quellcode des Projektes wird recht unbersichtlich. Um so schlimmer, je fter das Konstrukt darin vorkommt.


Hier die von mir erstellten Abstraktionen des Problems:
[url=https://forum.arduino.cc/index.php?topic=415229.msg2859554]Multitasking Macros [/url]
[url=https://forum.arduino.cc/index.php?topic=413734.msg2848523]Intervall Macro[/url]

An dieser Stelle ein herzliches Danke, an die Spender der (in den Threads versteckten) Anregungen.
Im folgenden habe ich versucht sie weitestgehend aufzunehmen.



Zum "WARUM?" bei mir ein Ersatzstoff, fr INTERVAL und die TaskMakros, her muss:

Eigentlich funktionieren diese Dinge prchtig!
Sind aber gewissen Einschrnkungen unterworfen, was hauptschlich an der Verwendung der statischen Daten liegt. Diese verhindern eine Mehrfachverwendung der erstellten Funktionen. Ich nenne solche Funktionen "Wegwerf Funktionen". Denn sie sind nur einmal pro Projekt nutzbar. Braucht man z.B. drei unabhngige Wechselblinker, dann muss man die Funktion kopieren, so dass man, im schlimmsten Fall, 3 vollstndig identische Kopien in einem Programm hat.
Ebenso verhlt es sich wenn man diese Makros in Kassen einbaut, dann ist nur eine Instanz nutzbar. Mehrere Instanzen fhren zu Fehlverhalten. Eine "Wegwerf Klasse".

Was im prozeduralen Umfeld gerade noch ertrglich erscheint, macht im OOP Umfeld die wichtigsten OO Mglichkeiten kaputt.
Dabei ist doch gerade die Wiederverwendbarkeit eins der vordringlichsten Ziele der OOP.

Ist klar geworden, wo der Hase im Pfeffer liegt?




Hier jetzt der neue Ansatz, um das BlinkWithoutDelay Problem zu erschlagen:
[b]BlinkWithSimpleTimer.ino[/b]
[code]#include "CombieTimer.h"

Combie::SimpleTimer timer; // timer Instanz anlegen

void setup()
{
  pinMode(LED_BUILTIN,OUTPUT);
}

void loop()
{
  if(timer(1000)) // wenn abgelaufen
  { 
    digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));
    // timer.start();
  }
}  [/code]

Ich nutze NameSpaces, da es schon eine SimpleTimer Lib gibt, und ich damit nicht kollidieren mchte. Wenn man sich den Code anschaut, dann ist mein SimpleTimer deutlich simpler, als der fremde SimpleTimer.

Draus leite ich, fr mich, das moralische Recht ab meinen SimpleTimer auch so zu nennen.
 :smiley-twist:  :smiley-twist: 


Erklrung:
Der SimpleTimer startet immer im abgelaufenen Zustand. Er wird also bei der ersten Nutzung true liefen. Will man das nicht, sollte man in setup() ein timer.start() notieren, so wie es im Beispiel vorbereitet ist.



Vergleichbares hier nochmal in einer abgewandelten Version:
[b]BlinkWithPulsator.ino[/b]
[code]
#include "CombieTimer.h"

Combie::Pulsator puls(1000); // liefert alle 1000 ms einmal true sonst false

void setup()
{
  pinMode(LED_BUILTIN,OUTPUT);
  puls.start(); // dank des start(), beginnt puls mit Pause
}

void loop()
{
  if(puls) digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));
}  [/code]


Eine Entprelleinrichtung, basierend auf dem SimpleTimer:
[b]TasterEntprellen.ino[/b]
[code]#include "CombieTimer.h"
using Combie::EntprellTimer;

const byte taster =  2; // taster gegen GND schaltend

EntprellTimer entprellen(200);   // in der Praxis wird man wohl eher 20ms verwenden

void setup()
{
 pinMode(LED_BUILTIN,OUTPUT);   
 pinMode(taster,INPUT_PULLUP);
} 

void loop()
{
  // die LED zeigt das, vom prellen bereinigte, Signal
  digitalWrite(LED_BUILTIN,entprellen(!digitalRead(taster)));  // invers, wg. pullup
} [/code]


Und zum Abschluss noch eine Aufgabe um ein Signal zu formen:
[b]Schaltsequenz.ino[/b]
[code]/**
 *    Ablaufdiagramm - Zeitdiagramm
 *    
 *        S1   _----------_____  Schalterstellung
 *        OU1  _-------------__  Verzoegertes abschalten
 *        OU2  ____-------_____  Verzoegertes einschalten
 *
*/

#include "CombieTimer.h"
using namespace Combie;

const byte S1   = 2; // Schalter Pin
const byte OUT1 = 3; // Ausgang
const byte OUT2 = 4; // Ausgang

EntprellTimer    entprell(20); // Schalter entprellen
RisingEdgeTimer  ton(500);     // steigende Flanke wird verzoegert
FallingEdgeTimer toff(500);    // abfallende Flanke wird verzoegert
 
void setup(void) 
{
  pinMode(S1,INPUT_PULLUP);
  pinMode(OUT1,OUTPUT);
  pinMode(OUT2,OUTPUT);
}

void loop(void) 
{
  bool schalter = entprell(!digitalRead(S1)); // invers wg. pullup
  digitalWrite(OUT1, toff(schalter));
  digitalWrite(OUT2, ton(schalter));
}[/code]


-------------------

Zu guter Letzt noch die [b]CombieTimer.h[/b]
[code]#pragma once

namespace Combie 
{
    
    
    class SimpleTimer
    {
      private:
      unsigned long timeStamp;
      bool abgelaufen = true; // default Status: timer abgelaufen
    
      public:
      void start()
      {
        timeStamp   = millis();
        abgelaufen  = false;
      }
    
      bool operator()(const unsigned long ablaufZeit) 
      {
        if(!abgelaufen) abgelaufen = millis() - timeStamp >= ablaufZeit;
        return abgelaufen;
      }
    };
    
    
    class Pulsator   // liefert alle X ms einen HIGH Impuls
    {
      protected: 
      SimpleTimer timer;
      const unsigned long interval;
      
      public:
      Pulsator(unsigned long interval):interval(interval){}
      
      void start()
      {
        timer.start();
      }
      
      operator bool()
      {
          bool result = false;
          if(timer(interval))
          {
            result = true;
            timer.start();
          }
          return result;
      } 
    };
    
    class EdgeTimer     // Die abstakte Mutter der Flankenverzoegerer
    {
      protected:
      SimpleTimer timer;
      unsigned long laufzeit = 0;
    
      public:
      explicit EdgeTimer(const unsigned long laufzeit):laufzeit(laufzeit){}
      
      virtual bool operator()(const bool trigger);
      
      void setLaufzeit(const unsigned long _laufzeit)
      {
         laufzeit =  _laufzeit;
      }
    };
    
    class FallingEdgeTimer: public EdgeTimer // abfallende Flanke wird verzoegert
    {
      public:
      using EdgeTimer::EdgeTimer;
      
      virtual bool operator()(const bool trigger)
      {
        if(trigger) timer.start();
        return !timer(laufzeit);
      }
    };
    
    class RisingEdgeTimer: public EdgeTimer // steigende Flanke wird verzoegert
    {
      public:
      using EdgeTimer::EdgeTimer;
      
      virtual bool operator()(const bool trigger)
      {
        if(!trigger) timer.start();
        return timer(laufzeit);
      }
    };
    
    class EntprellTimer
    {
      private:
      SimpleTimer timer;
      bool merker;  //  zustand
      const unsigned long entprellzeit;
      
      public:
      explicit EntprellTimer(const unsigned long entprellzeit) : entprellzeit(entprellzeit)  {}
      
      bool operator()(const bool trigger)
      {
         if(!(merker^trigger))   timer.start();
         if(timer(entprellzeit)) merker = !merker;
         return merker; 
      }
    };
    
}
[/code]

Die kann man einfach mit ins Projektverzeichnis werfen und wird dann von Arduino in einem Tab angezeigt.




Vorschlge?
Ansichten?
Kritik?
Wrde mich freuen!